﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;

namespace Hsenl.Network {
    public sealed class TcpClient : Connector {
        private TcpConnector _connector;
        private TcpSocket _socket;
        private TcpChannel _channel;

        public Configure Config { get; private set; }
        public Container Container { get; private set; }
        public override bool IsConnecting => this._connector.IsConnecting;
        public override bool IsConnected => this._channel != null;

        private byte[] keepAliveTime;

        private readonly object _locker = new();

        public TcpClient(Configure config) {
            this.Config = config ?? throw new ArgumentNullException(nameof(config));

            this.Container = new Container();
            this.Container.Let<TcpChannel>().AllowAutoInjection();
            this.Container.Let<TcpPacketReceiver>().As<IPacketReceiver>().ShareInjection();
            this.Container.Let<TcpPacketSender>().As<IPacketSender>().ShareInjection();
            this.Container.Let<TcpPacketReceiver>().As<IMessageReader>().ShareInjection();
            this.Container.Let<TcpPacketSender>().As<IMessageWriter>().ShareInjection();
        }

        public TcpClient(Configure config, Container container) {
            this.Config = config ?? throw new ArgumentNullException(nameof(config));
            this.Container = container ?? throw new ArgumentNullException(nameof(container));
        }

        /// <returns>0: 成功, 1: 正在连接, 2: 连接失败, -1: 已经连接了</returns>
        public override async HTask<int> ConnectAsync() {
            this.CheckDisposedException();
            var remoteEndPoint = this.Config.GetRemoteIPEndPoint();
            if (remoteEndPoint == null)
                throw new Exception("Remote IPEndPoint Invalid!");

            this._connector ??= new TcpConnector(remoteEndPoint.AddressFamily);

            var socket = this._connector.Socket;
            socket.ReceiveBufferSize = this.Config.RecvBufferSize;
            socket.SendBufferSize = this.Config.SendBufferSize;
            socket.NoDelay = this.Config.NoDelay;
            
            if (this.keepAliveTime == null) {
                this.keepAliveTime = BitConverter.GetBytes(1).Concat(BitConverter.GetBytes(30000)).Concat(BitConverter.GetBytes(5000)).ToArray();
            }

#if NET45_OR_GREATER
            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
            socket.IOControl(IOControlCode.KeepAliveValues, this.keepAliveTime, null);
#else
            // 跨平台框架：区分 Windows 和非 Windows 配置
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
                // Windows 用 IOControl 配置参数
                socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
                socket.IOControl(IOControlCode.KeepAliveValues, this.keepAliveTime, null);
            }
            else {
                // Linux/macOS 用 Tcp 特定选项配置参数（示例）
                const int TcpKeepAliveTime = 3;
                const int TcpKeepAliveInterval = 17;
                socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
                socket.SetSocketOption(SocketOptionLevel.Tcp, (SocketOptionName)TcpKeepAliveTime, 30000); // 空闲 30 秒开始探测
                socket.SetSocketOption(SocketOptionLevel.Tcp, (SocketOptionName)TcpKeepAliveInterval, 5000); // 探针间隔 5 秒
            }
#endif

            var ret = await this._connector.ConnectAsync(remoteEndPoint);
            if (ret != 0)
                return ret;

            this._channel = this.CreateChannel();
            if (this._channel == null)
                throw new Exception("Create Channel Fail!");

            this._socket ??= new();
            this._channel.Service = this;
            this._channel.UserToken = this._socket;
            this._channel.OnMessageReadedEvent += this.OnMessageReaded;
            this._channel.OnMessageWritedEvent += this.OnMessageWrited;
            this._channel.OnErrorEvent += this.OnChannelError;

            var receiver = new TcpReceiver(socket) {
                TcpRecvBufferSizePer = this.Config.RecvBufferSizePer
            };
            var sender = new TcpSender(socket) {
                TcpSendBufferSizePer = this.Config.SendBufferSizePer
            };
            this._socket.AddReceiver(receiver);
            this._socket.AddSender(sender);
            receiver.RecvBufferGetter += this._channel.GetRecvBuffer;
            sender.SendBufferGetter += this._channel.GetSendBuffer;
            receiver.OnRecvDataEvent += data => { this.OnChannelRecvData(this._channel.ChannelId, data); };
            sender.OnSendDataEvent += data => { this.OnChannelSendData(this._channel.ChannelId, data); };
            receiver.OnErrorEvent += this._channel.OnError;
            sender.OnErrorEvent += this._channel.OnError;
            
            this._channel.Init(0);
            
            receiver.ReceiveAsync();

            Log.Info($"客户端'{this._connector.Socket.LocalEndPoint}'连接到了服务器: {this._connector.Socket.RemoteEndPoint}");

            foreach (var plug in this.ForeachPlugs<IOnChannelStarted>()) {
                try {
                    plug.Handle(this._channel.ChannelId);
                }
                catch (Exception e) {
                    Log.Error(e);
                }
            }

            return ret;
        }

        private TcpChannel CreateChannel() {
            this.Container.StartStage();
            var channel = this.Container.Resolve<TcpChannel>();
            this.Container.EndStage();
            return channel;
        }

        protected override void OnChannelRecvData(long channelId, Memory<byte> data) {
            try {
                base.OnChannelRecvData(channelId, data);
            }
            finally {
                this._channel?.Read(data);
            }
        }

        protected override void OnChannelError(long channelId, int errorCode) {
            try {
                base.OnChannelError(channelId, errorCode);
            }
            finally {
                if (ErrorCode.fatalSocketErrors.Contains(errorCode) || errorCode > ErrorCode.SocketErrorWatershed) {
                    this.Disconnect(errorCode);
                }
            }
        }

        public override void Write(long channelId, byte[] data, int offset, int count) {
            if (!this.IsConnected) {
                return;
            }

            this._channel.Write(data, offset, count);
        }

        public override void Write(long channelId, Span<byte> data) {
            if (!this.IsConnected) {
                return;
            }

            this._channel.Write(data);
        }

        public override void StartSend(long channelId) {
            if (!this.IsConnected) {
                return;
            }

            this._socket.SendAsync();
        }

        public override void OnUpdate() {
            if (!this.IsConnected) {
                return;
            }

            try {
                this._channel.OnUpdate();
            }
            catch (Exception e) {
                Log.Error(e);
            }
        }

        public override void Disconnect(int error) {
            // 当channel发生错误导致被disconnect的时候, 是通过多线程调用的, 所以加锁
            lock (this._locker) {
                if (this._channel != null) {
                    Log.Info($"客户端{this._connector.Socket.LocalEndPoint}断开连接");
                    var channleId = this._channel.ChannelId;
                    this._channel.Dispose();
                    this._channel = null;
                    this._connector.Dispose();
                    this._connector = null;
                    this._socket.Dispose();
                    this._socket = null;

                    foreach (var plug in this.ForeachPlugs<IOnChannelDisconnected>()) {
                        try {
                            plug.Handle(channleId, error);
                        }
                        catch (Exception e) {
                            Log.Error(e);
                        }
                    }
                }
            }
        }

        public override Channel GetChannel(long channelId) {
            return this._channel;
        }

        public override ICollection<long> GetChannelIds() {
            return new Collection<long>() { this._channel.ChannelId };
        }

        public override void Dispose() {
            if (this.IsDisposed)
                return;

            base.Dispose();

            this.Disconnect(ErrorCode.ConnectionAborted);

            this.Config.Dispose();
            this.Config = null;
            this.Container.Dispose();
            this.Container = null;

            this._channel?.Dispose();
            this._channel = null;
            this._connector?.Dispose();
            this._connector = null;
            this._socket?.Dispose();
            this._socket = null;
        }

        public class Configure {
            public string RemoteIPHost { get; set; }
            public int Port { get; set; }
            public bool NoDelay { get; set; }
            public int RecvBufferSize { get; set; }
            public int SendBufferSize { get; set; }
            public int RecvBufferSizePer { get; set; }
            public int SendBufferSizePer { get; set; }

            public IPEndPoint GetRemoteIPEndPoint() {
                if (IPAddress.TryParse(this.RemoteIPHost, out var address)) {
                    var endPoint = new IPEndPoint(address, this.Port);
                    return endPoint;
                }

                return null;
            }

            public void Reset() {
                this.RemoteIPHost = null;
                this.Port = 0;
                this.NoDelay = false;
                this.RecvBufferSize = 0;
                this.SendBufferSize = 0;
                this.RecvBufferSizePer = 0;
                this.SendBufferSizePer = 0;
            }

            public void Dispose() {
                this.Reset();
            }
        }
    }
}